#pragma once

#include <iostream>
#include <string>
#include <functional>
#include <cassert>
#include <cstring>
#include <cerrno>
#include "Sock.hpp"
#include "Log.hpp"
#include "Err.hpp"
#include "Epoller.hpp"

namespace Yohifo
{
    static const uint16_t gport = 8888;
    static const uint16_t gnum = 64;

    using func_t = function<std::string(std::string)>;

    enum{ SIZE = 1024 };

    class EpollServer
    {
    public:
        EpollServer(const func_t &func, uint16_t port = gport)
            : port_(port)
            , func_(func)
        {
        }

        ~EpollServer()
        {
            listen_sock_.Close();
            epoller_.Close();
        }

        void InitServer()
        {
            // 初始化TCP服务器老三样
            listen_sock_.Socket();
            listen_sock_.Bind(port_);
            listen_sock_.Listen();
            epoller_.Create(gnum);

            // 先将监听套接字添加至 epoll 中
            bool ret = epoller_.Add(listen_sock_.getFd(), READ);
            assert(ret);
            (void)ret;
        }

        void StartServer()
        {
            // int timeout = 1000;
            // int timeout = 0;
            int timeout = -1;

            while (true)
            {
                int n = epoller_.Wait(revs_, gnum, timeout);
                switch (n)
                {
                case 0:
                    logMessage(Debug, "Timeout...");
                    break;
                case -1:
                logMessage(Warning, "epoll_wait fail... %d %s", errno, strerror(errno));
                    break;
                default:
                    logMessage(Info, "有 %d 个事件就绪了", n);
                    HandlerEvents(n);
                    break;
                }
            }
        }

        void HandlerEvents(int n)
        {
            for(int i = 0; i < n; i++)
            {
                int fd = revs_[i].data.fd;
                uint32_t event = revs_[i].events;

                //判断事件类型
                if(event & READ)
                {
                    //读取事件中，判断是否为连接请求
                    if(fd == listen_sock_.getFd())
                    {
                        //获取新的连接
                        std::string clientIP;
                        uint16_t clientPort;
                        int sock = listen_sock_.Accept(&clientIP, &clientPort);
                        if(sock == -1)
                            continue;
                        logMessage(Info, "IP:%s Port:%d 连接成功", clientIP.c_str(), clientPort);
                        
                        bool ret= epoller_.Add(sock, READ);
                        if(ret == false)
                        {
                            logMessage(Warning, "epoll_ctr(Add) fail %d %s", errno, strerror(errno));
                            close(sock);
                        }
                    }
                    else
                    {
                        //读取事件已就绪，进行读取
                        char buffer[SIZE] = { 0 };

                        ssize_t n = recv(fd, buffer, sizeof(buffer) - 1, 0);
                        if(n > 0)
                        {
                            buffer[n - 2] = '\0';
                            // std::cout << "client # " << buffer << std::endl;
                            // std::string request = (std::string(buffer) + " [epollServer send]\n");

                            std::string request = func_(buffer);

                            send(fd, request.c_str(), request.size(), 0);
                        }
                        else
                        {
                            //读取结束有不同情况
                            if(n == 0)
                                logMessage(Debug, "Recv done...");
                            else if(n == -1)
                                logMessage(Debug, "Recv fail %d %s", errno, strerror(errno));
                            
                            epoller_.Del(fd);
                            close(fd);
                        }
                    }
                }
                else if(event & WRITE)
                {}
                else
                {
                    //TODO
                }
            }
        }

    private:
        uint16_t port_;
        Sock listen_sock_;
        Epoller epoller_;
        struct epoll_event revs_[gnum];
        func_t func_;
    };
}